﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Animation;
using Red.Controls.Utils;

namespace Red.Controls.SourceControl
{
    public class ButtonInfo
    {
        #region Public Properties

        public Canvas Canvas { get; set; }

        public string ToolTip { get; set; }

        public object DragData { get; set; }
        public bool IsEnabled { get; set; } = true;

        #endregion Public Properties

        #region Events

        public delegate void ClickHandler();
        public event ClickHandler Click;

        #endregion Events

        #region Wrappers

        /// <summary>
        /// Because we can't invoke an event from outside the class
        /// </summary>
        internal void OnClick() { Click?.Invoke(); }

        #endregion Wrappers
    }

    public partial class InteractionTray : Window, IDragSource
    {
        #region Private Data

        private IList<UIElement> _elements = new List<UIElement>();

        #endregion Private Data

        #region Dependency Properties

        public static readonly DependencyProperty ButtonWidthProperty = DependencyProperty.Register( "ButtonWidth", typeof( int ), typeof( InteractionTray ) );
        public static readonly DependencyProperty ButtonHeightProperty = DependencyProperty.Register( "ButtonHeight", typeof( int ), typeof( InteractionTray ) );
        public static readonly DependencyProperty IconWidthProperty = DependencyProperty.Register( "IconWidth", typeof( int ), typeof( InteractionTray ), new PropertyMetadata( 24 ) );
        public static readonly DependencyProperty IconHeightProperty = DependencyProperty.Register( "IconHeight", typeof( int ), typeof( InteractionTray ), new PropertyMetadata( 24 ) );
        public static readonly DependencyProperty AnimateInProperty = DependencyProperty.Register( "AnimateIn", typeof( bool ), typeof( InteractionTray ) );

        #endregion Dependency Properties

        #region Public Properties

        public int ButtonHeight
        {
            get { return (int)GetValue( ButtonHeightProperty ); }
            set { SetValue( ButtonHeightProperty, value ); }
        }

        public int ButtonWidth
        {
            get { return (int)GetValue( ButtonWidthProperty ); }
            set { SetValue( ButtonWidthProperty, value ); }
        }

        public int IconHeight
        {
            get { return (int)GetValue( IconHeightProperty ); }
            set { SetValue( IconHeightProperty, value ); }
        }

        public int IconWidth
        {
            get { return (int)GetValue( IconWidthProperty ); }
            set { SetValue( IconWidthProperty, value ); }
        }

        public bool AnimateIn
        {
            get { return (bool)GetValue( AnimateInProperty ); }
            private set { SetValue( AnimateInProperty, value ); }
        }

        public ObservableCollection<ButtonInfo> Buttons { get; set; } = new ObservableCollection<ButtonInfo>();

        public double MaxDuration { get; set; } = 0.3;

        #endregion Public Properties

        #region C-Tor

        public InteractionTray()
        {
            InitializeComponent();

            Buttons.CollectionChanged += Buttons_CollectionChanged;
        }

        #endregion C-Tor

        #region Public Methods

        /// <summary>
        /// Convenience function for adding a new button
        /// </summary>
        public void AddButton( string canvas, ButtonInfo.ClickHandler clickHandler, string tooltip = null )
        {
            AddButton( (Canvas)FindResource( canvas ), clickHandler, tooltip );
        }

        /// <summary>
        /// Convenience function for adding a new button
        /// </summary>
        public void AddButton( Canvas canvas, ButtonInfo.ClickHandler clickHandler, string tooltip = null )
        {
            ButtonInfo btnInfo = new ButtonInfo()
            {
                Canvas = canvas,
                ToolTip = tooltip
            };

            Buttons.Add( btnInfo );
            btnInfo.Click += clickHandler;
        }

        public new void Close()
        {
            AnimateIn = false;

            AnimateOutButtons();
            DoubleAnimation anim = new DoubleAnimation( 0.0, TimeSpan.FromSeconds( MaxDuration ) );
            anim.Completed += Storyboard_Completed;
            BeginAnimation( OpacityProperty, anim );
        }

        #endregion Public Methods

        #region Private Methods

        private void AnimateRotation( int index, double angle, bool delay = false )
        {
            double percentage = ( index + 1 ) / (double)Buttons.Count;
            double duration = percentage * MaxDuration;
            double start = ( delay )? MaxDuration - duration : 0;

            DoubleAnimation anim = new DoubleAnimation( angle, TimeSpan.FromSeconds( duration ) )
            {
                BeginTime = TimeSpan.FromSeconds( start )
            };

            UIElement element = _elements[ index ];

            RotateTransform oldTransform = ( RotateTransform)element.RenderTransform;

            element.RenderTransform = new RotateTransform() { Angle = oldTransform.Angle };
            element.RenderTransform.BeginAnimation( RotateTransform.AngleProperty, anim );
        }

        private void AnimateOutButtons()
        {
            for ( int i = 0; i < Buttons.Count; ++i )
            {
                AnimateRotation( i, -90, true );
            }
        }

        #endregion Private Methods

        #region Event Handlers

        private void Buttons_CollectionChanged( object sender, NotifyCollectionChangedEventArgs e )
        {
            if( e.Action == NotifyCollectionChangedAction.Add )
            {
                foreach( ButtonInfo info in e.NewItems )
                {
                    _elements.Add( null );
                }
            }
        }

        private void Window_Deactivated( object sender, EventArgs e )
        {
            Close();
        }

        private void Window_Activated( object sender, EventArgs e )
        {
            AnimateIn = true;
        }

        private void Storyboard_Completed( object sender, EventArgs e )
        {
            base.Close();
        }

        private void Button_Loaded( object sender, RoutedEventArgs e )
        {
            FrameworkElement element = (FrameworkElement)sender;
            int index = Buttons.IndexOf( (ButtonInfo)element.DataContext );
            _elements[ index ] = element;
            AnimateRotation( index, 0 );
        }

        private void Button_Click( object sender, RoutedEventArgs e )
        {
            Button button = (Button)sender;
            ButtonInfo buttonInfo = (ButtonInfo)button.DataContext;

            buttonInfo.OnClick();

            Close();
        }

        #endregion Event Handlers

        #region IDragSource

        public DragDropEffects GetDragObject( Point point, out DependencyObject dragObject, out object userdata )
        {
            int index = (int)( point.Y / ButtonHeight );

            if( index < Buttons.Count && Buttons[ index ].DragData != null )
            {
                dragObject = _elements[ index ];
                userdata = Buttons[ index ].DragData;

                return DragDropEffects.Move;
            }

            dragObject = null;
            userdata = null;

            return DragDropEffects.None;
        }

        public void OnDragStart( DragData data )
        {
            Close();
        }

        public void OnDragStop( DragData data, DragDropEffects mode )
        {
            DragStop?.Invoke( data, mode );
        }

        #endregion IDragSource

        #region Events 

        public delegate void DragStopHandler( DragData data, DragDropEffects mode );
        public event DragStopHandler DragStop;

        #endregion Events 
    }
}
